﻿using Exceptionless;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json.Linq;
using Serilog;
using Serilog.Core;
using Serilog.Events;
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading.Tasks;

namespace Helpers
{
    public static class SerilogFactory
    {
        /// <summary>
        /// 初始化serilog默认配置,默认把日志保存到三个文件inf,wrn,err,错误日志默认显示在console
        /// </summary>
        public static void Init(IConfiguration configuration = default, Action<SerilogHelperOptions> configureOptions = default, Action<LoggerConfiguration> configureLogger = default)
        {
            try
            {
                var options = new SerilogHelperOptions()
                {
                    UserIdentity = AppDomain.CurrentDomain.FriendlyName
                };
                if (configureOptions != default)
                {
                    configureOptions(options);
                }
                ExceptionlessClient.Default.Configuration.SetUserIdentity(options.UserIdentity);

                var path = options.LogPath;
                if (string.IsNullOrEmpty(path) && RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
                {
                    //var directory = typeof(T).Assembly.GetName().Name;
                    var directory = AppDomain.CurrentDomain.FriendlyName;
                    path = $"c:\\logs\\{directory}\\";
                }
                else
                {
                    path = @"logs";
                }

                var conf = new LoggerConfiguration()
#if DEBUG
                .MinimumLevel.Verbose()
#else
                .MinimumLevel.Is(options.MinimumLevel)
#endif
                .MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
                .MinimumLevel.Override("Microsoft.Hosting", LogEventLevel.Information)
                //.MinimumLevel.Override("Microsoft.AspNetCore.Hosting", LogEventLevel.Information)
                .MinimumLevel.Override("System", LogEventLevel.Warning)
                .WriteTo.Debug();

                if (!HasConsoleLog(configuration) && (options.IsLogToConsole || Debugger.IsAttached))
                {
                    conf.WriteTo.Console();
                }
                configureLogger?.Invoke(conf);

                //从配置文件加载
                if (configuration != null) conf.ReadFrom.Configuration(configuration);

                conf.WriteTo.Logger(fileLogger =>
                   {
                       fileLogger
                       .Filter.ByIncludingOnly(xx =>
                       {
                           return xx.Level > LogEventLevel.Warning;
                       })
                       .WriteTo.Async(x =>
                       {
                           x.File(Path.Combine(path, $"log-{Process.GetCurrentProcess().Id}-.err"), rollingInterval: RollingInterval.Day, retainedFileCountLimit: null, outputTemplate: options.FileOutputTemplate);
                       });

                       if (!HasConsoleLog(configuration) && !options.IsLogToConsole && !Debugger.IsAttached)
                       {
                           fileLogger.WriteTo.Console();
                       }
                   })
                .WriteTo.Logger(fileLogger =>
                {
                    fileLogger
                    .Filter.ByIncludingOnly(xx =>
                    {
                        return xx.Level == LogEventLevel.Warning;
                    })
                    .WriteTo.Async(x =>
                    {
                        x.File(Path.Combine(path, $"log-{Process.GetCurrentProcess().Id}-.wrn"), rollingInterval: RollingInterval.Day, retainedFileCountLimit: null, outputTemplate: options.FileOutputTemplate);
                    });
                })
                .WriteTo.Logger(fileLogger =>
                {
                    fileLogger
                    .Filter.ByIncludingOnly(xx => xx.Level < LogEventLevel.Warning)
                    .WriteTo.Async(x =>
                    {
                        x.File(Path.Combine(path, $"log-{Process.GetCurrentProcess().Id}-.inf"), rollingInterval: RollingInterval.Day, retainedFileCountLimit: null, outputTemplate: options.FileOutputTemplate);
                    });
                });

                Log.Logger = conf.CreateLogger();
                Log.Information("serilog初始化完成");

                //记录全局未捕获异常
                if (options.IsLogUnhandledException)
                {
                    AppDomain.CurrentDomain.UnhandledException += options.UnhandledException;
                    TaskScheduler.UnobservedTaskException += options.UnobservedTaskException;
                }

                //程序退出时触发
                AppDomain.CurrentDomain.ProcessExit += (_, __) =>
                {
                    Log.CloseAndFlush();
                };
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
        }

        /// <summary>
        /// 判断是否需要添加Console
        /// </summary>
        private static bool HasConsoleLog(IConfiguration configuration)
        {
            var index = 0;
            do
            {
                var name = configuration?[$"Serilog:WriteTo:{index++}:Name"];
                if (string.IsNullOrEmpty(name))
                {
                    break;
                }
                if (name.Equals("console", StringComparison.OrdinalIgnoreCase))
                {
                    index = -1;
                    break;
                }
            } while (true);
            return index == -1;
        }
    }
}
